#include <TimeLib.h>
#include <Arduino.h>
#include "arduinoFFT.h"
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <EEPROM.h>
#include <NTPClient.h> // Date/Time manager
#include <WiFiUdp.h>                          // Library to send and receive UDP messages
#include <WiFiClient.h>                       // driver for WiFi client
#include "OneButton.h"
#include "audio_spectrum.h"
#include <WiFiManager.h>
#include "esp_task_wdt.h"
/********************FFT相关定义**********************************/
#define CHANNEL 5  //选择音频输入IO口序号为4
#define xres 32            // Total number of  columns in the display
#define yres 8            // Total number of  rows in the display

#define TIME_MODE 0
#define DAY_MODE 1
#define FFT_MODE 2
#define RAINING_MODE 3


unsigned long brightnessChangeTime = 0;


/*******************灯板参数定义*********************************/
#define LED_PIN     2  //灯板输入IO口选择
#define BRIGHTNESS  250  //默认背光亮度
#define LED_TYPE    WS2812  //灯珠类型
#define COLOR_ORDER GRB  //色彩顺序
#define buttonPin   19 //按键引脚

#define EEPROM_SIZE 5

#define EEPROM_BRIGHTNESS   0 //亮度
#define EEPROM_MODE         1 //模式
#define EEPROM_COLOR        2 //时钟颜色
#define EEPROM_PATTERN      3 //频谱样式
#define EEPROM_GAIN         4 //确定是否已存有数据

int brightnessNow = 250;
int Mode = 0;
int colorIndex = 22;
int displayPattern = 0;
int GlobalX = 0;              // 全局X，摄影机
int GlobalY = 0;              // 全局Y，摄影机

uint16_t hue = 240; //色调
uint8_t saturation = 100;//饱和度
uint8_t value = 100;//明度


// 代码雨配置 8列为一组 共四组
int columnIndex[8];
int stepIndex[8];
int glow[8];
bool Running[8]={false, false, false, false, false, false, false, false};
int stepsTillNext[8] = {0, 6, 2, 11, 4, 10, 7, 1};
int colorh = 120;//默认绿色

/*******************灯板参数定义*********************************/
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(xres, yres, 1, 1, LED_PIN,
                            NEO_MATRIX_TOP  + NEO_TILE_LEFT  + NEO_MATRIX_COLUMNS   + NEO_MATRIX_PROGRESSIVE +
                            NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
                            NEO_GRB + NEO_KHZ800);

//RGB颜色数组https://www.sojson.com/rgb.html

String  H;
String  M;
String  S;//秒
int D;//星期几 0为周日
int monthDay = 1;
int currentMonth = 1;
struct tm {
int tm_sec; // 秒，取值0~59；
int tm_min; // 分，取值0~59；
int tm_hour; // 时，取值0~23；
int tm_mday; // 月中的日期，取值1~31；
int tm_mon; // 月，取值0~11；
int tm_year; // 年，其值等于实际年份减去1900；
int tm_wday; // 星期，取值0~6，0为周日，1为周一，依此类推；
int tm_yday; // 年中的日期，取值0~365，0代表1月1日，1代表1月2日，依此类推；
int tm_isdst; // 夏令时标识符，实行夏令时的时候，tm_isdst为正；不实行夏令时的进候，tm_isdst为0；不了解情况时，tm_isdst()为负
};

bool colon = true;

String bitdata20[10]={
  "111101101101111","010110010010111",
  "111001111100111","111001111001111",
  "101101111001001","111100111001111",
  "111100111101111","111001001001001",
  "111101111101111","111101111001111"
};

WiFiUDP udp;
NTPClient timeClient(udp, "ntp1.aliyun.com", 60 * 60 * 8, 30 * 60 * 1000);
OneButton button(buttonPin, true);

WiFiManager wm;//esp32开发包使用2.0.2测试通过
void config_wifi()
{
  WiFi.mode(WIFI_STA);
  bool res;
  res = wm.autoConnect("ESP32C3-CLOCK");
  if (!res)
  {
    Serial.println("Failed to connect");
  }
  else
  {
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
}


void setup(){
  Serial.begin(115200);
  sampling_period_us = round(1000000*(1.0/samplingFrequency)); //计算采样频率
  pinMode(CHANNEL,INPUT); //初始化麦克风接口为输入模式，表示读取麦克风数据

  //setupWiFi();
  config_wifi();
  configTime(8 * 3600, 0,"ntp1.aliyun.com","ntp2.aliyun.com","ntp3.aliyun.com");
  EEPROM.begin(EEPROM_SIZE);
  // 如果值为123则判断存有预置值
  if (EEPROM.read(EEPROM_GAIN) != 123) {
    EEPROM.write(EEPROM_GAIN, 123);
    EEPROM.write(EEPROM_BRIGHTNESS, BRIGHTNESS);
    EEPROM.write(EEPROM_MODE, Mode);
    EEPROM.write(EEPROM_COLOR, colorIndex);
    EEPROM.write(EEPROM_PATTERN, displayPattern);
    EEPROM.commit();
  }else{
    brightnessNow = EEPROM.read(EEPROM_BRIGHTNESS);
    Mode = EEPROM.read(EEPROM_MODE);
    colorIndex = EEPROM.read(EEPROM_COLOR);
    displayPattern = EEPROM.read(EEPROM_PATTERN);
  }
  matrix.setBrightness(brightnessNow);
  button.attachClick(colorChange);
  button.attachDoubleClick(modeChange);
  button.attachMultiClick(statusSave);
  button.attachDuringLongPress(brightnessChange);
  Serial.println("WIFI online!");
}

//模式变更
void modeChange()
{
  int oldMode = Mode;
  if (++Mode > 3)Mode = 0;//模式变化
  int newMode = Mode;
  for(int i=0;i<8;i++){
    matrix.clear();
    GlobalY = -i;
    Mode = oldMode;
    switchMode(true);
    GlobalY = 8-i;
    Mode = newMode;
    switchMode(true);
    matrix.show();
    delay(16);
  }
  GlobalY = 0;
  Serial.println("按键切换模式");
  Serial.println(Mode);
  matrix.clear();
  switchMode(true);
}
//颜色变更
void colorChange()
{
  switch (Mode)
  {
    case TIME_MODE:
      if (++colorIndex > xres)colorIndex = 0;//颜色变化
      Serial.println("按键切换颜色");
      Serial.println(colorIndex);
      break;
    case FFT_MODE:
      if (++displayPattern > 2)displayPattern = 0;
      Serial.println("按键切换频谱样式");
      Serial.println(displayPattern);
      break;
    case RAINING_MODE:
      Serial.println("按键切换代码雨样式");
      break;
  }
  switchMode(true);

}
//亮度调节
void brightnessChange()
{
  unsigned long t = millis();
  if ((t - brightnessChangeTime) > 500) {
    brightnessNow += 20;
    if(brightnessNow > 250){
      brightnessNow = 20;
    }
    Serial.println("按键变更亮度");
    matrix.setBrightness(brightnessNow);
    matrix.show();
    brightnessChangeTime = millis();
  }
}
//设置保存
void statusSave(){
  Serial.println("按键保存配置");
  if (EEPROM.read(EEPROM_GAIN) != 123) {  // not equal to 123
    EEPROM.write(EEPROM_GAIN, 123);  // write value 123 to byte 256
  }
  EEPROM.write(EEPROM_BRIGHTNESS, brightnessNow);
  EEPROM.write(EEPROM_MODE, Mode);
  EEPROM.write(EEPROM_COLOR, colorIndex);
  EEPROM.write(EEPROM_PATTERN, displayPattern);
  EEPROM.commit();
  int x = xres;
  for(int i=0;i<52;i++){
    matrix.clear();
    matrix.fillScreen(0);
    matrix.setCursor(x, 0);
    matrix.print(F("Saved"));
    matrix.setTextColor(matrix.Color(148,0,211));
    if(--x < -36) {
      x = matrix.width();
    }
    matrix.show();
    esp_task_wdt_reset();
    delay(70);
  }
  matrix.clear();
  switchMode(true);
}

void showColon(int x,int y,bool l,uint16_t colorxy){
  String pstr = "101";
  for(int j = y; j < y+3; j = j + (1)){
    if (String(pstr.charAt(j-y)).toInt() != 0) {
      if(l){
        matrix.drawPixel(x,j,colorxy);
      }else{
        matrix.drawPixel(x,j,matrix.Color(0, 0, 0));
      }
    } else {
      matrix.drawPixel(x,j,matrix.Color(0, 0, 0));
    }
  }
}

void showMouthDay(bool implement) {
  if ( millis() - lastTime > 2000 | implement)
  {
    timeClient.update();
    //将epochTime换算成年月日
    struct tm timeinfo;
    if(!implement){
      if (!getLocalTime(&timeinfo))
      {
          Serial.println("Failed to obtain time");
          //return;
      }
      Serial.println(&timeinfo, "%F %T %A");
      monthDay = timeinfo.tm_mday;
      currentMonth = timeinfo.tm_mon + 1;
    }
    Serial.println(monthDay);
    Serial.println(currentMonth);
    lastTime = millis();
  }
  String monthDayStr = monthDay>9? String(monthDay):('0'+String(monthDay));
  String currentMonthStr = currentMonth>9? String(currentMonth):('0'+String(currentMonth));
  showbitnumber(currentMonthStr,3,5,7+GlobalX,2+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value));
  showbitnumber(monthDayStr,3,5,17+GlobalX,2+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value));
  matrix.drawPixel(15+GlobalX,6+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value));

}


void showTime(bool implement)
{
  timeClient.update();
  H = timeClient.getFormattedTime().substring(0, 2);
  M = timeClient.getFormattedTime().substring(3, 5);
  S = timeClient.getFormattedTime().substring(6, 8);
  D = timeClient.getDay();
  if ( millis() - lastTime > 1000 | implement)
  {
    if(!implement){
      lastTime = millis();
    }
    showbitnumber(H,3,5,2+GlobalX,1+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value));
    showbitnumber(M,3,5,12+GlobalX,1+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value));
    showbitnumber(S,3,5,22+GlobalX,1+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value));
    //冒号
    showColon(10+GlobalX,2+GlobalY,true,hsv2rgb(colorIndex*(360/xres), saturation, value-40));
    showColon(20+GlobalX,2+GlobalY,true,hsv2rgb(colorIndex*(360/xres), saturation, value-40));
    //星期
    showWeek(2+GlobalX,7+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value-40));
    showWeekDay(2+GlobalX,7+GlobalY,hsv2rgb(colorIndex*(360/xres), saturation, value));
    //matrix.show();
  }
}

void showbitnumber(String number, int xlength, int ylength, int x, int y, uint16_t colorxy){
  if(number.toInt()<10){
    showbitmap(bitdata20[(int)(0)],xlength,ylength, x, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, x+4, y, colorxy);
  }else if(number.toInt()<100){
    showbitmap(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, x, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, x+4, y, colorxy);
  }else if(number.toInt()<1000){
    showbitmap(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, x, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, x+4, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(2)).toInt() + 1) - 1)],xlength,ylength, x+8, y, colorxy);
  }else if(number.toInt()<10000){
    showbitmap(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, x, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, x+4, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(2)).toInt() + 1) - 1)],xlength,ylength, x+8, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(3)).toInt() + 1) - 1)],xlength,ylength, x+12, y, colorxy);
  }else if(number.toInt()<100000){
    showbitmap(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, x, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, x+4, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(2)).toInt() + 1) - 1)],xlength,ylength, x+8, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(3)).toInt() + 1) - 1)],xlength,ylength, x+12, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(4)).toInt() + 1) - 1)],xlength,ylength, x+16, y, colorxy);
  }else if(number.toInt()<1000000){
    showbitmap(bitdata20[(int)((String(number.charAt(0)).toInt() + 1) - 1)],xlength,ylength, x, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(1)).toInt() + 1) - 1)],xlength,ylength, x+4, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(2)).toInt() + 1) - 1)],xlength,ylength, x+8, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(3)).toInt() + 1) - 1)],xlength,ylength, x+12, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(4)).toInt() + 1) - 1)],xlength,ylength, x+16, y, colorxy);
    showbitmap(bitdata20[(int)((String(number.charAt(5)).toInt() + 1) - 1)],xlength,ylength, x+20, y, colorxy);
  }
}


void showWeek(int x, int y, uint16_t colorxy) {
  for(int i=0;i<7;i++){
    drawFastXLine(x+i*4,y,3,colorxy);
  }
}

void showWeekDay(int x, int y, uint16_t colorxy) {
  int d = D;
  if(D == 0){
    d = 7;
  }
  drawFastXLine(x+(d-1)*4,y,3,colorxy);
}

void showbitmap(String bitrgbstr, int xlength, int ylength, int x, int y, uint16_t colorxy) {
  //Serial.println("bitrgbstr = " + bitrgbstr);
  for (int i = x; i < x+(xlength); i = i + (1)) {
    for(int j = y; j < y+(ylength); j = j + (1)){
      if (String(bitrgbstr.charAt(((j-y)*xlength+i-x))).toInt() != 0) {
        matrix.drawPixel(i,j,colorxy);
      } else {
        matrix.drawPixel(i,j,matrix.Color( 0, 0, 0));
      }
    }
  }
}

void showFFT(){
    //进行采样
    getSamples();
    //更新频谱柱
    displayUpdate(&matrix, displayPattern);
    //matrix.show();
    delay(1);
}


void drawFastXLine(int16_t x, int16_t y, int16_t h, uint16_t c){
  for(int i=x;i<x+h;i++){
    matrix.drawPixel(i,y,c);
  }
}

void drawFastYLine(int16_t x, int16_t y, int16_t h, int16_t c){
  for(int i=y;i<y+h;i++){
    matrix.drawPixel(x,i,c);
  }
}

void movingPixel(int x, int y, int colorh, int backgroundGlow = 0){
    int pixel = y;
    matrix.drawPixel(x, pixel, hsv2rgb(colorh, 100, 100 ));
    if(pixel-1 >= 0){ matrix.drawPixel(x, pixel-1, hsv2rgb(colorh, 80, 80 ));}
    if(pixel-2 >= 0){ matrix.drawPixel(x, pixel-2, hsv2rgb(colorh, 70, 70 ));}
    if(pixel-3 >= 0){ matrix.drawPixel(x, pixel-3, hsv2rgb(colorh, 45, 45 ));}
    if(pixel-4 >= 0){ matrix.drawPixel(x, pixel-4, hsv2rgb(colorh, 25, 25 ));}
    if(pixel-5 >= 0){ matrix.drawPixel(x, pixel-5, hsv2rgb(colorh, 10, 10 ));}
    if(pixel-6 >= 0){ matrix.drawPixel(x, pixel-6, matrix.Color(0, 0, 0));}
}

void showRainingCode(bool implement) {
  if ( millis() - lastTime > 100 | implement)
  {
    if(!implement){
      lastTime = millis();
    }
    for(int i=0; i<8; i++){
      if(stepIndex[i] > stepsTillNext[i]){
        Running[i] = true;
        stepsTillNext[i] = 13;  // 进行数组初始化
        columnIndex[i] = random((i*4), ((i+1)*4));
        glow[i] = random(0, 2);     //随机背景置灰值
        stepIndex[i] = 0;
      }
      if(Running[i] == true){
        movingPixel(columnIndex[i]+GlobalX, stepIndex[i]+GlobalY, colorh, glow[i]);
        if(stepIndex[i] == 13){
          Running[i] = false;
        }
      }
      stepIndex[i] += 1;
    }
    //matrix.show();
  }
}

uint16_t hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value)
{
    uint8_t red = 0;
    uint8_t green = 0;
    uint8_t blue = 0;
    uint16_t hi = (hue / 60) % 6;
    uint16_t F = 100 * hue / 60 - 100 * hi;
    uint16_t P = value * (100 - saturation) / 100;
    uint16_t Q = value * (10000 - F * saturation) / 10000;
    uint16_t T = value * (10000 - saturation * (100 - F)) / 10000;

    switch (hi)
    {
    case 0:
        red = value;
        green = T;
        blue = P;
        break;
    case 1:
        red = Q;
        green = value;
        blue = P;
        break;
    case 2:
        red = P;
        green = value;
        blue = T;
        break;
    case 3:
        red = P;
        green = Q;
        blue = value;
        break;
    case 4:
        red = T;
        green = P;
        blue = value;
        break;
    case 5:
        red = value;
        green = P;
        blue = Q;
        break;
    default:
        return matrix.Color(255, 0, 0);
    }
    red = red * 255 / 100;
    green = green * 255 / 100;
    blue = blue * 255 / 100;
    return matrix.Color(red, green, blue);
}

void switchMode(bool change){
  switch (Mode)
  {
    case TIME_MODE:
      showTime(change);
      break;
    case DAY_MODE:
      showMouthDay(change);
      break;
    case FFT_MODE:
      showFFT();
      break;
    case RAINING_MODE:
      showRainingCode(change);
      break;
  }
}

void loop()
{
  button.tick();
  switchMode(false);
  delay(16);
  matrix.show();
}
